C++14后，变量也可以通过特定类型参数化，成为做变量模板。

\begin{tcolorbox}[colback=webgreen!5!white,colframe=webgreen!75!black]
\hspace*{0.75cm}我们有非常相似的术语来描述非常不同的事情:变量模板是一个变量，它是一个模板(变量在这里是一个名词)。可变参数模板是用于可变数量模板参数的模板(可变参数在这里是形容词)。
\end{tcolorbox}

例如，可以使用以下代码定义$\pi$的值，而不定义值的类型:

\begin{lstlisting}[style=styleCXX]
template<typename T>
constexpr T pi{3.1415926535897932385};
\end{lstlisting}

与所有模板一样，此声明不能出现在函数或块作用域内。

要使用变量模板，必须指定它的类型。例如，下面的代码使用了声明pi<>作用域的两个不同变量:

\begin{lstlisting}[style=styleCXX]
std::cout << pi<double> << '\n';
std::cout << pi<float> << '\n';
\end{lstlisting}

也可以声明用于不同翻译单元的变量模板:

\begin{lstlisting}[style=styleCXX]
// header.hpp:
template<typename T> T val{}; // zero initialized value

// translation unit 1:
#include "header.hpp"

int main()
{
	val<long> = 42;
	print();
}

// translation unit 2:
#include "header.hpp"

void print()
{
	std::cout << val<long> << '\n'; // OK: prints 42
}
\end{lstlisting}

变量模板也可以有默认模板参数:

\begin{lstlisting}[style=styleCXX]
template<typename T = long double>
constexpr T pi = T{3.1415926535897932385};
\end{lstlisting}

可以使用默认或其他类型:

\begin{lstlisting}[style=styleCXX]
std::cout << pi<> << '\n'; // outputs a long double
std::cout << pi<float> << '\n'; // outputs a float
\end{lstlisting}

但请注意，必须始终指定尖括号。仅使用pi就会导致错误:

\begin{lstlisting}[style=styleCXX]
std::cout << pi << '\n'; // ERROR
\end{lstlisting}

变量模板也可以用非类型参数进行参数化，非类型参数也可以用来对初始化式进行参数化。例如:

\begin{lstlisting}[style=styleCXX]
#include <iostream>
#include <array>

template<int N>
std::array<int,N> arr{}; // array with N elements, zero-initialized

template<auto N>
	constexpr decltype(N) dval = N; // type of dval depends on passed value

int main()
{
	std::cout << dval<'c'> << '\n'; // N has value 'c' of type char
	arr<10>[0] = 42; // sets first element of global arr
	for (std::size_t i=0; i<arr<10>.size(); ++i) { // uses values set in arr
		std::cout << arr<10>[i] << '\n';
	}
}
\end{lstlisting}

同样需要注意的是，即使在不同的翻译单元中对同一变量std::array<int,10> arr进行初始化和迭代时，仍使用全局作用域。

\hspace*{\fill} \\ %插入空行
\noindent
\textbf{数据成员的变量模板}

变量模板的一种应用是定义表示类模板成员的变量。例如，若类模板定义如下:

\begin{lstlisting}[style=styleCXX]
template<typename T>
class MyClass {
	public:
	static constexpr int max = 1000;
};
\end{lstlisting}

允许为MyClass<>的不同特化定义不同的值，然后可以定义

\begin{lstlisting}[style=styleCXX]
template<typename T>
int myMax = MyClass<T>::max;
\end{lstlisting}

这样就可以直接写

\begin{lstlisting}[style=styleCXX]
auto i = myMax<std::string>;
\end{lstlisting}

而不是

\begin{lstlisting}[style=styleCXX]
auto i = MyClass<std::string>::max;
\end{lstlisting}

对于标准类，如

\begin{lstlisting}[style=styleCXX]
namespace std {
	template<typename T> class numeric_limits {
		public:
		...
		static constexpr bool is_signed = false;
		...
	};
}
\end{lstlisting}

可以定义

\begin{lstlisting}[style=styleCXX]
template<typename T>
constexpr bool isSigned = std::numeric_limits<T>::is_signed;
\end{lstlisting}

可以这样写

\begin{lstlisting}[style=styleCXX]
isSigned<char>
\end{lstlisting}

而不是

\begin{lstlisting}[style=styleCXX]
std::numeric_limits<char>::is_signed
\end{lstlisting}

\hspace*{\fill} \\ %插入空行
\noindent
\textbf{类型特征后缀\_v}

C++17后，标准库使用变量模板技术为标准库中产生(布尔)值的所有类型特征定义快捷方式。比如，

\begin{lstlisting}[style=styleCXX]
std::is_const_v<T> // since C++17
\end{lstlisting}

而不是

\begin{lstlisting}[style=styleCXX]
std::is_const<T>::value // since C++11
\end{lstlisting}

标准库定义为

\begin{lstlisting}[style=styleCXX]
namespace std {
	template<typename T> constexpr bool is_const_v = is_const<T>::value;
}
\end{lstlisting}






















